#include <iostream>
#include <pthread.h> //原生线程库
#include <unistd.h>
#include <thread>
#include <string>
#include <time.h>
using namespace std;
#define THREAD_NUM 5
class ThreadData
{
public:
    ThreadData(const string& n,pthread_mutex_t* pm)
        :name(n)
        ,pmtx(pm)
    {}
public:
    string name;
    pthread_mutex_t* pmtx;

};

// 如果多线程访问同一个全局变量，并对它进行数据计算，多线程会相互影响吗？
// 在并发访问的时候导致了我们数据不一致
int tickets = 10000;// 临界资源，现在还不能保证任意时刻只允许一个执行流访问
// pthread_mutex_t mtx; // pthread_mutex_t是原生线程库提供的数据类型 
// 加锁的时候我们希望加锁的力度越小越好，不要讲一些无关紧要的代码放入临界区

// 加锁之后，线程在临界区里，依然会被OS切换，但不会有问题
// 因为线程虽然被切换了，但是是持有锁被切换的，其他抢票的线程要执行临界区的代码
// 必须先申请锁，但此时锁无法申请成功。
// 所以不会让其他线程进入临界区，也就保证了临界区中的数据一致性
// 我是一个线程，我不申请锁，就只是访问临界区 -- 错误的编码方式
// 初级原子性的体现：对于没有持有锁的线程看来对我有利的只有两种情况：1.线程1没有持有锁(什么都没做)2.线程1释放锁(做完)，此时我可以申请锁
// 加了锁后执行临界区代码一定是串行的

// 要访问临界资源，每个线程必须申请一把锁，每个线程必须先看到(访问)同一把锁，锁本身是不是就是一种共享资源
// 谁来保证锁的安全？为了保证锁的安全，锁的申请和释放必须是原子的。 自己保护，在设计时通过一行汇编进行保护
void *getTickets(void *args)
{
    ThreadData* td = (ThreadData*)args; 
    while (true)
    {
        // 临界区
        pthread_mutex_lock(td->pmtx);
        if (tickets > 0) // 临界区 1.判断的本质也是一种计算，逻辑计算，CPU只能进行算术运算和逻辑运算
        {
            usleep(1000);
            printf("%s : %d\n",td->name.c_str(),tickets);
            tickets--;// 2.也可能出现
            pthread_mutex_unlock(td->pmtx);
        }
        else
        {
            pthread_mutex_unlock(td->pmtx);
            break;
        }
    }
    return nullptr;
}

int main()
{
    pthread_mutex_t mtx;
    pthread_mutex_init(&mtx,nullptr);
    pthread_t t[THREAD_NUM];
    for(int i = 0;i < THREAD_NUM;i++)
    {
        string name = "thread ";
        name += to_string(i + 1);
        ThreadData* td = new ThreadData(name,&mtx);
        pthread_create(t+i,nullptr,getTickets,(void *)td);

    }
    for(int i = 0;i <THREAD_NUM;i++)
    {
        pthread_join(t[i],nullptr);
    }
    pthread_mutex_destroy(&mtx);    
    return 0;
}









// int main()
// {
//     pthread_t t1, t2, t3;
//     // 多线程抢票的逻辑
//     pthread_create(&t1, nullptr, getTickets, nullptr);
//     pthread_create(&t2, nullptr, getTickets, nullptr);
//     pthread_create(&t3, nullptr, getTickets, nullptr);

//     pthread_join(t1, nullptr);
//     pthread_join(t2, nullptr);
//     pthread_join(t3, nullptr);
    
//     return 0;
// }
